import traceback
import math, random, datetime, time, uuid, hashlib
from datetime import datetime
from bson.objectid import ObjectId


# 异常捕获
def capture_error(func):
    def do(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except:
            print(f"{func.__name__} error-->>{str(traceback.format_exc())}")
            return

    return do


# 深拷贝
def deep_copy(src):
    if isinstance(src, dict):
        dst = {}
        for k, v in src.items():
            dst[k] = deep_copy(v)
        return dst

    elif isinstance(src, (list, tuple)):
        dst = []
        for i in src:
            dst.append(deep_copy(i))
        return dst
    else:
        return src


class Common:
    def __init__(self):
        self.easy_call()

    # 公共简称
    def easy_call(self):
        self.STR = self.formatString
        self.DATE = self.getDate
        self.NOW = self.getNowTime
        self.WEEK = self.getWeek
        self.HOUR = self.getHour
        self.RANDARR = self.getRandArr
        self.RAND = self.isRandNum
        self.RANDINT = self.getRandNum
        self.RANDLIST = self.getRandList
        self.ZERO = self.getZeroTime
        self.TTL = self.getTTLTime
        self.MNOW = self.getNowMillisecond
        self.YDAY = self.getYearDay
        self.random = random

    def getObjectId(self, is_str=False):
        if is_str:
            return str(ObjectId())
        return ObjectId()

    # 向上取整
    @staticmethod
    def ceil(num):
        return math.ceil(num)

    # 在范围内随机一个整数
    def getRandNum(self, minNum, maxNum):
        _num = random.randint(minNum, maxNum)
        return _num

    # 在范围内随机一个浮点数
    def getRandFloat(self, minNum, maxNum):
        _num = random.uniform(minNum, maxNum)
        return _num

    # 在数组中随机N个元素
    def getRandList(self, rand_list, num=1):
        if num > len(rand_list):
            num = len(rand_list)

        return random.sample(rand_list, num)

    # 获取过期时间对象
    def getTTLTime(self, nt=None):
        return datetime.now(t=nt)

    # 将时间格式转换成时间戳，默认返回当前时间戳
    def getNowTime(self, nt=None):
        if nt is None:
            return int(time.time())

        _fmt = '%Y-%m-%d'
        if len(nt.split(' ')) > 1:
            _fmt = '%Y-%m-%d %H:%M:%S'

        return int(time.mktime(time.strptime(nt, _fmt)))

    # 获取当前时间毫秒
    def getNowMillisecond(self):
        _nitem = time.time() * 1000
        return int(_nitem)

    # mongo的_id转时间戳
    def objectId2Time(self, objectid):
        result = 0

        try:
            result = time.mktime(objectid.generation_time.timetuple())
        except:
            pass

        return result

    # 获取指定时间的日期格式
    def getDate(self, nt=None, fmtStr='%Y-%m-%d', many=False, month=False):
        if nt == None:
            nt = self.getNowTime()

        if many:
            fmtStr = "%Y-%m-%d %H:%M:%S"

        if month:
            fmtStr = "%Y-%m"

        return time.strftime(fmtStr, time.localtime(int(nt)))

    # 获取小时
    def getHour(self, fmtStr='%H'):
        return int(self.getDate(fmtStr=fmtStr))

    # 根据时间戳返回为一年中的第几周(默认星期一为第一天)
    def getWeekNumByTime(self, timestamp, st='%W'):
        # %U 一年中的星期数（00-53）星期天为星期的开始
        # %W 一年中的星期数（00-53）星期一为星期的开始
        _chkTime = timestamp
        if st == '%W': _chkTime = self.getWeekFirstDay(_chkTime)
        return self.getDate(_chkTime, '%Y-') + time.strftime(st, time.localtime(int(_chkTime)))

    # 根据时间戳返回星期几
    def getWeek(self, nt=None):
        if nt == None:
            nt = self.getNowTime()
        _week = int(time.strftime('%w', time.localtime(nt)))
        return _week

    # 根据时间配置获取本周一0点开始对应的时间
    def getNowByWeekAndTime(self, timeconfig):
        _weekInitTime = self.getWeekFirstDay(self.NOW())
        for k, v in timeconfig.items():
            if k == "week":
                _weekInitTime += (v - 1) * 24 * 3600
            elif k == "hour":
                _weekInitTime += v * 3600
            elif k == "minute":
                _weekInitTime += v * 60
            elif k == "second":
                _weekInitTime += v

        return _weekInitTime

    # 检测指定日期之间的差异(默认天数)
    def getDateDiff(self, sdate=None, edate=None, w='d'):
        if sdate == None:
            sdate = self.getNowTime()
        if edate == None:
            edate = self.getNowTime()
        _sdata = self.getDate(sdate, '%Y-%m-%d')
        _edata = self.getDate(edate, '%Y-%m-%d')
        _diff = self.dataDiff(_sdata, _edata, w)
        return _diff

    # 获取每周第一天的日期时间戳
    def getWeekFirstDay(self, timestamp):
        _dateObj = time.localtime(int(timestamp))
        _weekDay = int(_dateObj.tm_wday)
        _nt = self.getNowTime(self.getDate(timestamp))
        _firstDay = _nt - (_weekDay * 60 * 60 * 24)
        return _firstDay

    # 获取每周最后一天24点的日期时间戳
    def getWeekLastDay(self, timestamp):
        return self.getWeekFirstDay(timestamp) + 7 * 24 * 3600

    # 获取该时间的零点时间戳
    def getZeroTime(self, time):
        _retVal = 0
        if isinstance(time, str):
            _retVal = self.NOW(self.DATE(self.NOW(time)))
        elif isinstance(time, int):
            _retVal = self.NOW(self.DATE(time))

        return _retVal

    # 获取唯一标识
    def getUniqCode(self):
        _code = str(uuid.uuid1()).split('-')
        _res = _code[3] + _code[0][4:]
        return str(_res)

    # 返回MD5加密32为小写字符串
    def md5(self, val):
        return hashlib.md5(str(val).encode()).hexdigest()

    def is_chinese(self, uchar):
        '''
        判断一个unicode是否是汉字
        '''
        if uchar >= u'\u4e00' and uchar <= u'\u9fa5':
            return True
        else:
            return False

    def is_number(self, uchar):
        '''
        判断一个unicode是否是数字
        '''
        return uchar.isdigit()

    def is_alphabet(self, uchar):
        '''
        判断一个unicode是否是英文字母
        '''
        if (uchar >= u'\u0041' and uchar <= u'\u005a') or (uchar >= u'\u0061' and uchar <= u'\u007a'):
            return True
        else:
            return False

    # 过滤内容中指定字符
    def FilterChat(self, msg):
        if type(msg) != type(''):
            return msg

        _chatArr = {'%': '％', '&': '＆', '\\n': '', '\n': ''}
        for k, v in _chatArr.items():
            msg = msg.replace(k, v)
        return msg

    # 根据数字参数获取一个32位递进的code
    def getUnCode(self, _cid):
        _ct = int(_cid)
        _codeArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k',
                    'm',
                    'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
        _codeNum = len(_codeArr)
        _rArr = []
        for i in range(6):
            _tmp = divmod(_ct, _codeNum)
            _rArr.append(_tmp[1])
            if _tmp[0] < _codeNum:
                _rArr.append(_tmp[0])
                break
            _ct = _tmp[0]

        _res = ''
        _codeLen = len(_rArr)
        for o in range(_codeLen):
            _idx = _rArr[o]
            if _codeLen == 2 and _idx == 0 and o == 1:
                continue

            _res = _codeArr[_idx] + _res

        return _res

    # 替换字符串参数
    def formatString(self, strobj, *args):
        _res = strobj
        _idx = 1
        for d in args:
            _key = '{%s}' % str(_idx)
            _res = _res.replace(_key, str(d))
            _idx += 1

        return _res

    # 是否随机到范围
    def isRandNum(self, base, pro):
        _randNum = random.randint(1, base)
        if _randNum <= pro:
            return True
        return False

    # 随机获取数组中的某一项
    def getRandArr(self, arr, base, pkey="p"):
        _rNum = random.randint(1, base)
        _tmpNum = 0
        for item in arr:
            _tmpNum += item[pkey]
            if _rNum <= _tmpNum:
                return deep_copy(item)

    # 随机获取数组中的某几项
    def getRandArrNum(self, arr, num, base=None):
        if base is None:
            base = sum([x['p'] for x in arr])
        _randNum = self.RANDINT(1, base)
        _sump = 0
        itemlist = []
        flag = 0
        for idx, ele in enumerate(arr):
            _sump += ele['p']
            if _randNum <= _sump:
                num -= 1
                itemlist.append(ele)
                flag = idx
                break

        _newarr = arr[0: flag] + arr[flag + 1:]
        # 已结束
        if num == 0 or not _newarr:
            return itemlist

        else:
            itemlist += self.getRandArrNum(_newarr, num, base=base)

        return itemlist

    # 检测字符串是否为空
    def chkStrIsNull(self, chkstr):
        _chatArr = {' ': '', '　': '', '    ': ''}
        for k, v in _chatArr.items():
            chkstr = chkstr.replace(k, v)
        if chkstr == "":
            return 1
        return 0

    # 去空格tab
    def trimStr(self, chkstr):
        _chatArr = {' ': '', '　': '', '    ': ''}
        _res = chkstr
        for k, v in _chatArr.items():
            _res = chkstr.replace(k, v)

        return chkstr

    # 检测空格
    def chkTrimStr(self, chkstr):
        _chatArr = {' ': '', '　': '', '    ': ''}
        for k, v in _chatArr.items():
            if chkstr.find(k) > -1:
                return 1

        return 0

    # 判断是否是同一天
    # sec过期时间修正值(秒)
    # ntime:当前时间
    # chktime:目标时间
    # sec 主要用于不以0点为分割点时的修正的秒数，如每日凌晨5点判为第二天的话，则sec = 5*60*60 = 18000
    def chkSameDate(self, ntime, chktime=None, sec=0):
        _ntime = ntime - sec
        if not chktime:
            chktime = self.NOW()
        _chktme = chktime - sec
        if self.getDate(_chktme) == self.getDate(_ntime):
            return True

        return False

    # 拷贝变量
    def dcopy(self, data):
        return deep_copy(data)

    # 检测指定时间戳的时间日差
    # chktime 表示从几点开始为1天
    def getTimeDiff(self, _nt, _et, _chktime=0):
        def fmtTime(t):
            t = self.DATE(t)
            sArr = t.split(' ')
            dArr = sArr[0].split('-')
            if len(sArr) > 1:
                tArr = sArr[1].split(':')
                return datetime(int(dArr[0]), int(dArr[1]), int(dArr[2]), int(tArr[0]), int(tArr[1]),
                                int(tArr[2]))
            else:
                return datetime(int(dArr[0]), int(dArr[1]), int(dArr[2]))

        _d1 = fmtTime(_nt)
        _d2 = fmtTime(_et)
        _diff = (_d1 - _d2).days
        _zt = self.NOW(self.DATE())
        # 如果当前时间小于chktime则是前一天
        if _nt <= _zt + _chktime:
            _diff -= 1

        if _diff < 0:
            _diff = 0

        return _diff

    def getYearDay(self):
        _year, _month, _day = self.DATE().split('-')
        _year, _month, _day = int(_year), int(_month), int(_day)
        days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        if (_year % 4 == 0 and _year % 100 != 0) or (_year % 400 == 0):
            days[1] = 29

        _sum = _day
        i = 0
        while i < _month - 1:
            _sum += days[i]
            i += 1

        return _sum

    # 根据MONGON数据库_id字符生成6位数字字符串
    # tid一定要是mongodb的_id格式
    def getUUIDByDBID(self, tid):
        _code = tid[-6:]
        _intCode = str(int(_code, 16))
        return _intCode[1:]

    # 去掉不是中文的字符
    def is_ustr(self, in_str):
        out_str = ''
        for i in range(len(in_str)):
            if self.is_chinese(in_str[i]):
                out_str = out_str + in_str[i]
            else:
                out_str = out_str + ''
        return out_str

    # 获取本月的最后一天
    def getMonthByLast(self, nt=None):
        if nt is None:
            _time = self.NOW()
        _splitInfo = self.DATE(nt).split("-")

        first_day = datetime.date(int(_splitInfo[0]), int(_splitInfo[1]), int(_splitInfo[2]))
        next_month = first_day.replace(day=28) + datetime.timedelta(days=4)

        return self.getNowTime(str(next_month - datetime.timedelta(days=next_month.day)))

    # 获取本月第一天时间戳
    def getMonthByFirst(self, nt=None):
        if nt is None:
            _time = self.NOW()
        _splitInfo = self.DATE(nt).split("-")

        first_day = datetime.date(int(_splitInfo[0]), int(_splitInfo[1]), 1)

        return self.getNowTime(str(first_day))
